/**
 * Copyright (C) 2010 Cloudfarming <info@cloudfarming.nl>
 *
 * Licensed under the Eclipse Public License - v 1.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.eclipse.org/legal/epl-v10.html
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package nl.cloudfarming.client.geoviewer.wms;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListCellRenderer;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import org.geotools.data.ows.CRSEnvelope;
import org.geotools.data.ows.Layer;
import org.geotools.data.ows.OperationType;
import org.geotools.data.ows.StyleImpl;
import org.geotools.data.wms.WMSUtils;
import org.geotools.data.wms.WebMapServer;
import org.geotools.data.wms.request.GetMapRequest;


/**
 * This lab explores the use of the GeoTools WMS client code adapted by Jody
 * Garnett's demo.
 * 
 * @author Hasan Turksoy
 */
public class WMSLab extends JFrame {
        private static final long serialVersionUID = -3039255518595806472L;

        /** Original coverage we are working on */
        WebMapServer wms;
        CoveragePanel panel;
        Image image;
        JList layers;
        JButton getMapButton;
        JComboBox styleCombo;
        /**
         * Map<Layer,Style> as controlled by styleCombo box
         */
        Map styles = new HashMap();
        Layer selectedLayer = null;

        DefaultComboBoxModel availableStyles;

        /**
         * Explore the functionality of the provided GridCoverage (think
         * BufferedImage + CRS).
         * <p>
         * A GridCoverage literally a set of features that "covers" an area without
         * gaps; in the case of grid coverage the area is covered by an regular
         * grid.
         * <p>
         * Coverage work by letting you call a "sample" operation in order to
         * retrieve a Record of the data at the location. A grid coverage lets you
         * express the location using row and column.
         * <p>
         * 
         * @param coverage
         */
        public WMSLab(WebMapServer server) {
                this.wms = server;
                setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                setLayout(new BorderLayout());

                String title = getWMSTitle(wms);
                setTitle(title);

                Set good = WMSUtils.getQueryableLayers(wms.getCapabilities());
                this.layers = new JList(good.toArray());
                layers.setCellRenderer(new LayerCellRenderer());
                layers.getSelectionModel().addListSelectionListener(
                                new ListSelectionListener() {
                                        public void valueChanged(ListSelectionEvent e) {
                                                setSelectedLayer((Layer) layers.getSelectedValue());
                                        }
                                });
                JScrollPane scrollPane = new JScrollPane(layers);
                scrollPane.setPreferredSize(new Dimension(200, 400));
                add(scrollPane, BorderLayout.WEST);

                this.panel = new CoveragePanel();
                add(panel, BorderLayout.CENTER);

                JPanel buttons = new JPanel();
                getMapButton = new JButton("GetMap");
                getMapButton.addActionListener(new ActionListener() {
                        public void actionPerformed(ActionEvent e) {
                                getMap();
                        }
                });
                buttons.add(getMapButton);
                availableStyles = new DefaultComboBoxModel();
                styleCombo = new JComboBox(availableStyles);
                styleCombo.addItemListener(new ItemListener() {
                        public void itemStateChanged(ItemEvent e) {
                                if (selectedLayer != null) {
                                        styles.put(selectedLayer, styleCombo.getSelectedItem());
                                }
                        }
                });
                styleCombo.setRenderer(new StyleCellRenderer());
                buttons.add(styleCombo);

                add(buttons, BorderLayout.NORTH);
                pack();
        }

        private String getWMSTitle(WebMapServer wms) {
                return null;
        }

        public void setSelectedLayer(Layer selected) {
                availableStyles.removeAllElements();
                this.selectedLayer = selected;
                if (selected != null) {
                        // availableStyles.addElement("default");
                        List styles = selected.getStyles();
                        for (Iterator i = styles.iterator(); i.hasNext();) {
                                StyleImpl style = (StyleImpl) i.next();
                                availableStyles.addElement(style);
                        }
                }
                styleCombo.repaint();
        }

        class StyleCellRenderer extends DefaultListCellRenderer {
                private static final long serialVersionUID = 7421698429520525469L;

                public Component getListCellRendererComponent(JList list, Object value, // value
                                // to
                                // display
                                int index, // cell index
                                boolean iss, // is the cell selected
                                boolean chf) // the list and the cell have the focus
                {
                        super.getListCellRendererComponent(list, value, index, iss, chf);
                        StyleImpl style = (StyleImpl) value;
                        if (style == null)
                                return this;

                        String title = style.getTitle() == null ? style.getName() : style
                                        .getTitle().toString();
                        setText(title);
                        return this;
                }

                Icon getIcon(StyleImpl style) {
                        List urlList = style.getLegendURLs();
                        if (urlList == null || urlList.isEmpty())
                                return null;

                        URL url = (URL) style.getLegendURLs().get(0);
                        ImageIcon icon = new ImageIcon(url);
                        return icon;
                }
        }

        class LayerCellRenderer extends DefaultListCellRenderer {
                private static final long serialVersionUID = 1173012107250651733L;
                HashMap icons = new HashMap();

                public Component getListCellRendererComponent(JList list, Object value, // value
                                // to
                                // display
                                int index, // cell index
                                boolean iss, // is the cell selected
                                boolean chf) // the list and the cell have the focus
                {
                        super.getListCellRendererComponent(list, value, index, iss, chf);
                        Layer layer = (Layer) value;
                        setText(layer.getTitle());
                        Icon glyph;
                        if (icons.containsKey(layer)) {
                                glyph = (Icon) icons.get(layer);
                        } else {
                                glyph = getLegendGraphics(layer);
                                icons.put(layer, glyph);
                        }
                        setIcon(glyph);
                        setToolTipText(layer.getName());
                        return this;
                }
        }

        public Icon getLegendGraphics(Layer layer) {
                return null;
        }

        private String getNamedStyle(Layer layer) {
                Object layerStyle = styles.get(layer);
                String namedStyle = null;
                if (layerStyle == null) {
                        namedStyle = null;
                } else if (layerStyle instanceof String) {
                        namedStyle = (String) layerStyle;

                } else if (layerStyle instanceof StyleImpl) {
                        StyleImpl style = (StyleImpl) layerStyle;
                        namedStyle = style.getName();
                }
                return namedStyle;
        }

        public void getMap() {
        try {
            Layer selected = (Layer) layers.getSelectedValue();
            
            List<Layer> layers = new ArrayList<Layer>(1);
            layers.add(selected);
            
            getMap(layers);
        } catch (Exception e1) {
            image = null;
        }
    }

        private void getMap(Layer layer) throws Exception {
                panel.repaint();
        }

        private void getMap(List layers) throws Exception {
                if (layers == null || layers.isEmpty())
                        return;

                GetMapRequest mapRequest = wms.createGetMapRequest();
                CRSEnvelope bounds = null;
                for (Iterator i = layers.iterator(); i.hasNext();) {
                        Layer layer = (Layer) i.next();
                        String namedStyle = getNamedStyle(layer);
                        mapRequest.addLayer(layer, namedStyle);
                        CRSEnvelope box = layer.getLatLonBoundingBox();
                        box.setEPSGCode("EPSG:4326");
                        if (box != null) {
                                if (bounds != null) {
                                        bounds.setMinX(Math.min(bounds.getMinX(), box.getMinX()));
                                        bounds.setMaxX(Math.max(bounds.getMaxX(), box.getMaxX()));
                                        bounds.setMinY(Math.min(bounds.getMinY(), box.getMinY()));
                                        bounds.setMaxY(Math.max(bounds.getMaxY(), box.getMaxY()));
                                } else {
                                        bounds = box;
                                }
                        }
                }
                if (bounds == null) {
                        bounds = new CRSEnvelope();
                        bounds.setEPSGCode("EPSG:4326");
                }
                mapRequest.setFormat(getImageFormat(wms));

                mapRequest.setSRS(bounds.getEPSGCode());
                mapRequest.setBBox(bounds);
                mapRequest.setDimensions(panel.getWidth(), panel.getHeight());

                URL url = mapRequest.getFinalURL();
                System.out.println("requesting layers : " + url);
                ImageIcon load = new ImageIcon(url);
                image = load.getImage();
                panel.repaint();
        }

        /**
         * Some WMS Servers like GeoServer support strange formats like KML and PDF
         * 
         * @return
         */
        String getImageFormat(WebMapServer wms) {
                OperationType description = wms.getCapabilities().getRequest()
                                .getGetMap();

                List formats = description.getFormats();
                for (Iterator i = formats.iterator(); i.hasNext();) {
                        String format = (String) i.next();
                        if (format.indexOf("pdf") != -1)
                                continue;

                        return format;
                }
                return "image/jpeg";
        }

        /**
         * Search through the layer data structure for bounds for the provided srs
         * (if null we will return the first bounds).
         * 
         * @param srs
         * @return CRSEnvelope
         */
        CRSEnvelope getCRSEnvelope(Layer layer, String srs) {

                if (layer == null)
                        return null;
                Map boundingBoxes = layer.getBoundingBoxes();
                if (boundingBoxes != null && !boundingBoxes.isEmpty()) {
                        for (Iterator i = boundingBoxes.entrySet().iterator(); i.hasNext();) {
                                Map.Entry entry = (Entry) i.next();
                                String layerSrs = (String) entry.getKey();
                                CRSEnvelope box = (CRSEnvelope) entry.getValue();
                                if (srs == null || srs.equals(layerSrs)) {
                                        return box;
                                }
                        }
                }
                return getCRSEnvelope(layer.getParent(), srs);
        }

        class CoveragePanel extends JPanel {
                private static final long serialVersionUID = -4755270758709990530L;

                CoveragePanel() {
                        setBackground(Color.WHITE);
                }

                public Dimension getPreferredSize() {
                        return new Dimension(640, 400);
                }

                public void paintComponent(Graphics graphics) {
                        super.paintComponents(graphics);
                        if (image != null) {
                                Graphics2D g = (Graphics2D) graphics;
                                g.drawImage(image, 0, 0, null);
                        }
                }
        }

        /**
         * Prompt the user for a file and open up ImageLab.
         * 
         * @param args
         *            filename of image
         */
        public static void main(String[] args) throws Exception {
                URL server = getServerURL(args);
                WebMapServer wms;

                System.out.println("Connecting to " + server);
                wms = new WebMapServer(server);

                System.out.println("Welcome");
                WMSLab wmsLab = new WMSLab(wms);
                wmsLab.setVisible(true);
        }

        public static String[] getLayerNames(WebMapServer wms) {
                Layer[] namedLayers = WMSUtils.getNamedLayers(wms.getCapabilities());
                String[] names = new String[namedLayers.length];
                for (int i = 0; i < namedLayers.length; i++) {
                        Layer layer = namedLayers[i];
                        names[i] = layer.getName();
                }
                return names;
        }

        public static URL getServerURL(String[] args) throws MalformedURLException {
                if (args.length == 0) {
                        Object[] servers = new Object[] {
                                        "http://localhost:8080/geoserver/wms?service=WMS&request=GetCapabilities",
                                        "http://www2.dmsolutions.ca/cgi-bin/mswms_gmap?Service=WMS&VERSION=1.1.0&REQUEST=GetCapabilities",
                                        "http://wms.jpl.nasa.gov/wms.cgi?Service=WMS&Version=1.1.1&Request=GetCapabilities",
                                        "http://giswebservices.massgis.state.ma.us/geoserver/wms?service=WMS&request=GetCapabilities",
                                        "http://wms.cits.rncan.gc.ca/cgi-bin/cubeserv.cgi?VERSION=1.1.0&REQUEST=GetCapabilities",
                                        "http://atlas.gc.ca/cgi-bin/atlaswms_en?VERSION=1.1.1&Request=GetCapabilities&Service=WMS", 
                                        "http://gdsc.nlr.nl/wms/lufo2005?request=GetCapabilities&service=WMS&version=1.1.1",
                        };
                        Object selected = JOptionPane.showInputDialog(null,
                                        "WMS GetCapabilities URL", "Choose a WMS Server",
                                        JOptionPane.QUESTION_MESSAGE, null, servers, null);

                        if (selected == null)
                                System.exit(0);

                        return new URL((String) selected);
                } else {
                        return new URL(args[0]);
                }
        }
}

